﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.Diagnostics;
using System.Speech.Synthesis;
using System.Threading;

namespace WPFVDRUtility
{
    /// <summary>
    /// Interaction logic for VDRUtil.xaml
    /// </summary>
    public partial class VDRUtil : Window
    {
        // Set up the Speech
        SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();

        // Set up the serial port
        static SerialPort _serialPort = null;

        // Set up the event handler for the COM Ports event
        private event RoutedEventHandler _comPorts = null;
        private event RoutedEventHandler _updateTerminal = null;

        // Set up the event handler for the COM Port Settings
        private event SelectionChangedEventHandler _comSettings = null;

        private Thread readThread = null;

        static bool _continue;

        public VDRUtil()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // _speechSynthesizer.Rate = 1;
            // _speechSynthesizer.SpeakAsync("The time is " + DateTime.Now.ToShortTimeString());

            // Init the serial port
            // Create a new SerialPort object with default settings.
            _serialPort = new SerialPort();          

            // List out the COM Port
            ListComPorts(this, new RoutedEventArgs());

            // Register event for any setting change  
            _comSettings += new SelectionChangedEventHandler(ComSettingsChanged);
            this.comboBoxCom.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);
            this.ComBoBaud.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);
            this.ComBoBits.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);
            this.ComBoFlow.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);
            this.ComBoParity.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);
            this.ComBoStopBit.SelectionChanged += new SelectionChangedEventHandler(ComSettingsChanged);

            // Register event for the message for terminal
            _updateTerminal += new RoutedEventHandler(UpdateTerminal);

            // Start the read thread
            _continue = true;
            readThread = new Thread(ReadTerminal);           
            readThread.Start();

            MakeIndeterminate(sender, e);
        }

        private void MakeIndeterminate(object sender, RoutedEventArgs e)
        {        
            // statusBar.Items.Clear();
            ProgressBar progbar = new ProgressBar();
            progbar.Background = Brushes.Gray;
            progbar.Foreground = Brushes.Red;
            progbar.Width = 600;
            progbar.Height = 15;
            progbar.IsIndeterminate = true;
            statusBar.Items[1] = progbar;
            // statusBar.Items.Add(progbar);
        }

        /// <summary>
        /// This Event Handler is used to fill out the available COM Ports
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListComPorts(Object sender, RoutedEventArgs e)
        {
            comboBoxCom.BeginInit();
            comboBoxCom.Items.Clear();
            foreach (string s in SerialPort.GetPortNames())
            {
                // try to close the existing port
                if (_serialPort.IsOpen)
                {
                    try
                    {
                        _serialPort.Close();
                    }
                    catch (Exception exClose) {
                        Debug.WriteLine(exClose.Message);
                    }
                }                                

                // try to assign the value and open the port
                // if the port can be opened, then it can be added to the combobox
                // otherwise, continue
                _serialPort.PortName = s;
                try
                {
                    _serialPort.Open();                 
                    comboBoxCom.Items.Add(s);
                    _serialPort.Close();
                }
                catch (Exception exOpen)
                {
                    Debug.WriteLine(exOpen.Message);
                }
            }
            comboBoxCom.EndInit();

            // Select the first item.
            comboBoxCom.SelectedIndex = 0;
        }

        /// <summary>
        /// This Event Handler is called when the Windows is closed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closed(object sender, EventArgs e)
        {
            // Close the serial port
            if (_serialPort.IsOpen)
            {
                try
                {
                    _serialPort.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }
            _continue = false;
            this.readThread.Join();
        }

        private void buttonTest_Click(object sender, RoutedEventArgs e)
        {
            if (!UpdateComSettings(sender)) return;
            try
            {
                _serialPort.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {

            }
        }

        private bool UpdateComSettings(Object sender)
        {
            try
            {
                if (_serialPort.IsOpen) _serialPort.Close();
                // _serialPort.PortName = comboBoxCom.SelectionBoxItem.ToString();
                _serialPort.PortName = "COM7";              
                _serialPort.BaudRate = int.Parse(ComBoBaud.SelectionBoxItem.ToString());
                _serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), ComBoParity.SelectionBoxItem.ToString());
                _serialPort.DataBits = int.Parse(ComBoBits.SelectionBoxItem.ToString());
                
                // assign the stop bit
                string stopbit = ComBoStopBit.SelectionBoxItem.ToString();
                if (stopbit.Equals("1")) _serialPort.StopBits = StopBits.One;
                else if (stopbit.Equals("1.5")) _serialPort.StopBits = StopBits.OnePointFive;
                else if (stopbit.Equals("2")) _serialPort.StopBits = StopBits.Two;
                
                // assign the handshake
                string handshake = ComBoFlow.SelectionBoxItem.ToString();
                if(handshake.Equals("None")) _serialPort.Handshake = Handshake.None;
                else if(handshake.Equals("Hardware")) _serialPort.Handshake = Handshake.RequestToSend;
                else if(handshake.Equals("Xon/Xoff")) _serialPort.Handshake = Handshake.XOnXOff;

                _serialPort.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            return true;
        }

        private void ComSettingsChanged(object sender, SelectionChangedEventArgs e)
        {
            UpdateComSettings(sender);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Hide or Show the Hex editor
            if (this.TextBoxTerminalHex.IsVisible)
                this.TextBoxTerminalHex.Visibility = Visibility.Hidden;
            else
                this.TextBoxTerminalHex.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// This API will read the information from the COM port
        /// and fill in the text control
        /// </summary>
        public void ReadTerminal()
        {
            while (_continue)
            {
                try
                {
                    if (_serialPort.IsOpen)
                    {
                        string str = _serialPort.ReadExisting();
                        if (!String.IsNullOrEmpty(str))
                        {
                            Debug.Write(str);
                            
                            // Invoke the dispatch
                            // Application.Current.Dispatcher.Invoke(
                            //    _updateTerminal,
                            //    str,
                            //    new RoutedEventArgs());

                            Application.Current.Dispatcher.BeginInvoke(
                                _updateTerminal,
                                str,
                                new RoutedEventArgs());
                        }
                    }
                }
                catch (Exception e)
                {
                    // Debug.WriteLine(e.Message);
                }
                Thread.Sleep(10);
            }
        }

        private void UpdateTerminal(Object sender, RoutedEventArgs e)
        {
            string type = sender.GetType().ToString();
            // Use reflection to figure out the type
            if (type.Equals("System.String"))
            {
                string str = (string) sender;

                // no processing is required if the string is empty or null
                if (String.IsNullOrEmpty(str)) return;

                TextBoxterminal.AppendText(str);
                TextBoxterminal.ScrollToEnd();

                string hex = "";
                foreach (char c in str)
                {
                    int tmp = c;
                    if (c == '\r')
                    {
                        hex += '\n';
                    }
                    else
                    {
                        hex += String.Format(" {0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
                    }
                }               
                TextBoxTerminalHex.AppendText(hex);
                TextBoxTerminalHex.ScrollToEnd();
            }
        }


        /// <summary>
        /// This API is used to get the input command from the user
        /// Then it should call the event handler to send the command over
        /// and ready to pick up the receive code
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBoxterminal_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                // The user presses the enter key, it is time to get the line content
                // and send over to the COM port for processing
                string str = TextBoxterminal.GetLineText(TextBoxterminal.LineCount - 2);

                // Split the input
                char[] chars = new char[2];
                chars[0] = '$';
                chars[1] = '>';
                string[] codes = str.Split(chars);

                // Get the command line 
                string command = codes[codes.Length - 1];
                command.Trim();
            }           
        }

        /// <summary>
        /// Exit the application
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnExit(object sender, RoutedEventArgs e)
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnAbout(object sender, RoutedEventArgs e)
        {

        }
    }
}
